/*
* linux-3.0.8/drivers/media/video/gt2440_camdev.c
*/


#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/bug.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/ratelimit.h>
#include <linux/videodev2.h>

#include <asm/mach-types.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>

#include <plat/cpu.h>
#include <plat/gpio-cfg.h>

#include <media/v4l2-ctrls.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-device.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-mediabus.h>


#include "gt2440_ov9650.h"
#include "gt2440_cam.h"


static void gt2440_cam_get_gpios(int *gpio_start, int *gpio_reset)
{
	*gpio_start = S3C2410_GPJ(0);
	*gpio_reset = S3C2410_GPJ(12);
}

static int gt2440_cam_gpio_get(void)
{
	int gpio_start, gpio_reset;
	int ret, i;
	gt2440_cam_get_gpios(&gpio_start, &gpio_reset);
	for (i = 0; i < GT2440_CAM_NUM_GPIOS; i++) {
		int gpio = gpio_start + i;
		if (gpio == gpio_reset)
			continue;
		ret = gpio_request(gpio, "cam");
		if (!ret)
			ret = s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
		if (ret) {
			pr_err("failed to configure GPIO %d\n", gpio);
			for (--i; i >= 0; i--)
				gpio_free(gpio--);
			return ret;
		}
		s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
	}
	return 0;
}
static void gt2440_cam_gpio_put(void)
{
	int i, gpio_start, gpio_reset;
	gt2440_cam_get_gpios(&gpio_start, &gpio_reset);
	for (i = 0; i < GT2440_CAM_NUM_GPIOS; i++) {
		int gpio = gpio_start + i;
		if (gpio != gpio_reset)
			gpio_free(gpio);
	}
}




static int gt2440_ov9650_init_gpio(struct gt2440_ov9650_platdata *pdata)
{
	int gpio = pdata->gpio_pwdn;
	int ret=0;
	if (gpio_is_valid(gpio)){
		ret = gpio_request(gpio, "ov9650 power");
		if (ret < 0){
			printk(KERN_ERR "failed to request gpio: %d\n", gpio);
			return ret;
		}
		if (gpio_is_valid(gpio)) {
			gpio_set_value(gpio, 1);
			gpio_export(gpio, 0);
		}
	}
	gpio = pdata->gpio_reset;
	if (gpio_is_valid(gpio)){
		ret = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "ov9650 reset");
		if (ret < 0){
			printk(KERN_ERR "failed to request gpio: %d\n", gpio);
			goto exit_1;
		}
		if (gpio_is_valid(gpio)) {
			gpio_set_value(gpio, 1);
			gpio_export(gpio, 0);
		}
	}
	return 0;
	
exit_1:
	gpio_free(pdata->gpio_pwdn);
	return ret;
}
static int gt2440_ov9650_exit_gpio(struct gt2440_ov9650_platdata *pdata)
{	
	gpio_free(pdata->gpio_pwdn);
	gpio_free(pdata->gpio_reset);
	return 0;
}
static int gt2440_ov9650_set_power(struct gt2440_ov9650_platdata *pdata, 
									int on)
{
	if (on) {
		gpio_set_value(pdata->gpio_pwdn, 0);
		gpio_set_value(pdata->gpio_reset, 0);
		usleep_range(25000, 26000);
	} else {
		gpio_set_value(pdata->gpio_pwdn, 1);
		gpio_set_value(pdata->gpio_reset, 1);
	}
	return 0;
}

/***** platf data structure  *****/
static struct gt2440_ov9650_platdata gt2440_ov9650_pdata  = {
	.mclk_frequency	= 24000000UL,
	.gpio_reset	= S3C2410_GPJ(12),
	.gpio_pwdn	= S3C2410_GPG(12),

	.init_gpio = gt2440_ov9650_init_gpio ,
	.exit_gpio = gt2440_ov9650_exit_gpio,
	.set_power = gt2440_ov9650_set_power,
};

static struct gt2440_cam_platdata gt2440_cam_pdata  = {
	.i2c_board_info	= {
			.type = "OV9650",
			.addr = 0x60 >> 1,
			.platform_data = &gt2440_ov9650_pdata,
		},
	.clock_frequency	= 24000000U,
	.mbus_type		= V4L2_MBUS_PARALLEL,
	.flags			= V4L2_MBUS_PCLK_SAMPLE_RISING |
					  V4L2_MBUS_VSYNC_ACTIVE_HIGH |
					  V4L2_MBUS_HSYNC_ACTIVE_LOW,
	.i2c_bus_num		= 0,
	
	.gpio_get = gt2440_cam_gpio_get,
	.gpio_put = gt2440_cam_gpio_put,
};

static struct resource gt2440_cam_resource[] = {
	[0] = {
		.start = GT2440_PA_CAMIF,
		.end   = GT2440_PA_CAMIF + GT2440_SZ_CAMIF - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_S3C2440_CAM_C,
		.end   = IRQ_S3C2440_CAM_C,
		.flags = IORESOURCE_IRQ,
	},
	[2] = {
		.start = IRQ_S3C2440_CAM_P,
		.end   = IRQ_S3C2440_CAM_P,
		.flags = IORESOURCE_IRQ,
	}

};


/***** platform device *****/
static u64 gt2440_cam_dmamask = 0xffffffffUL;
static void gt2440_platform_device_release(struct device *dev){}
static struct platform_device gt2440_cam_device = {
	.name = PLAT_DEVICE_NAME,
	.id = -1,
	.num_resources	  = ARRAY_SIZE(gt2440_cam_resource),
	.resource	  = gt2440_cam_resource,
	.dev		= {
		.release = gt2440_platform_device_release,
		.platform_data = &gt2440_cam_pdata,
		.dma_mask = &gt2440_cam_dmamask,
		.coherent_dma_mask = 0xffffffffUL
	},
};


static int __init gt2440_camdev_init(void)
{	printk(KERN_INFO"gt2440_camdev_init()\n");

	return platform_device_register(&gt2440_cam_device);
}
static void __exit gt2440_camdev_exit(void)
{	printk(KERN_INFO"gt2440_camdev_exit()\n");

	platform_device_unregister(&gt2440_cam_device);
}

module_init(gt2440_camdev_init);
module_exit(gt2440_camdev_exit);
MODULE_DESCRIPTION("GT2440	Camera Device Register Driver");
MODULE_AUTHOR("Liguang13579<1659890447@qq.com>");
MODULE_LICENSE("GPL v2");

